package org.ecpkn.PAGridSim.core.report;

import gridsim.GridResource;
import gridsim.Gridlet;
import gridsim.GridletList;
import gridsim.ResourceCharacteristics;

import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.ecpkn.PAGridSim.core.configuration.Configuration;
import org.ecpkn.PAGridSim.core.gridsimEntity.paMachine.PAMachine;
import org.ecpkn.PAGridSim.core.gridsimEntity.paMachine.PAMachineList;
import org.ecpkn.PAGridSim.core.gridsimEntity.paMachine.PAMachineStatut;
import org.ecpkn.PAGridSim.core.offlineOptimization.ChangingWorkloadOptimizationAlgo;
import org.ecpkn.PAGridSim.core.offlineOptimization.OptimizedResultEntry;
import org.ecpkn.PAGridSim.core.tasksCreation.Job;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.gantt.Task;
import org.jfree.data.gantt.TaskSeries;
import org.jfree.data.gantt.TaskSeriesCollection;
import org.jfree.ui.TextAnchor;

public class AnalyseTools {
	private static final Logger logger = LogManager
			.getLogger(AnalyseTools.class);
	private static SortedSet<Double> sortedTimePoints;

	// TODO:create automaticaly report adress
	public static final class Const {
		public static final int START_SIMULATION_TIME = 0;
	}

	static {
		String mode = Configuration.getInstance().getParameterString(
				Configuration.Const.TASKS_ARRIVING_MODE);
		if (Configuration.Const.ARRIVING_MODE_START_TOGETHER.equals(mode)) {
			Object[] timePoints = ChangingWorkloadOptimizationAlgo.sortedTimePoints
					.toArray();
			Double secondPoint = (Double) timePoints[1];
			sortedTimePoints = ChangingWorkloadOptimizationAlgo.sortedTimePoints
					.subSet(0.0, secondPoint);
		} else {
			sortedTimePoints = ChangingWorkloadOptimizationAlgo.sortedTimePoints;
		}
	}

	public static double printGridletListDetailHistory(GridletList list,
			Boolean printSucceed) {
		double missingRate = 0;
		;
		int failed = 0;
		int successful = 0;
		int prob = 0;
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i).getGridletStatus() == Gridlet.FAILED) {
				logger.trace(list.get(i).getGridletID()
						+ list.get(i).getGridletHistory());
				failed = failed + 1;
			} else if (list.get(i).getGridletStatus() == Gridlet.SUCCESS) {
				if (printSucceed) {
					logger.trace(list.get(i).getGridletID()
							+ list.get(i).getGridletHistory());
				}
				successful = successful + 1;
			} else {
				prob = prob + 1;
				logger.trace(list.get(i).getGridletID()
						+ list.get(i).getGridletHistory());
			}
		}

		logger.trace("Gridlets succeed: " + successful + "; Gridlets failed: "
				+ failed + "; Gridlets with unknown problem: " + prob);

		missingRate = Configuration.getInstance().toDecimalFormatDouble(
				(failed + prob) / (double) (failed + successful + prob));
		return missingRate;
	}

	/**
	 * Prints the Gridlet objects
	 * 
	 * @param list
	 *            list of Gridlets
	 */
	public static void printGridletListResume(GridletList list, String name) {
		StringBuilder sb = new StringBuilder();
		String indent = "\t";
		sb.append("\n========== OUTPUT for " + name + " ==========\n");
		sb.append("No\tGdlID" + indent + "STATUS" + indent + indent
				+ "End time" + indent + "deadline\n");
		Gridlet gridlet;
		String status;
		Job j;
		// for each user entity, prints out the results. Prints out the
		// table of summary
		for (int i = 0; i < list.size(); i++) {
			gridlet = (Gridlet) list.get(i);
			j = (Job) gridlet;
			String gridletId = Configuration.getInstance()
					.toDecimalFormatIntString(gridlet.getGridletID());
			sb.append(i + indent + gridletId + indent);
			status = gridlet.getGridletStatusString();
			sb.append(status);
			String endT = Configuration.getInstance().toDecimalFormat(
					j.getFinishTime());
			String deadLine = Configuration.getInstance().toDecimalFormat(
					j.getDeadline_());
			sb.append(indent + indent + endT + indent + deadLine + "\n");
		}
		logger.trace(sb.toString());
	}

	/**
	 * print single execution trace like: achine ID 0 is used by GridLet ID
	 * 330004** between 9591.7390 and 9592.6114,its deadline is9730.9766
	 */
	public static void printExecutionTrace(ResourceCharacteristics resource) {
		PAMachine pm;
		ExecutionTraceHashMap trace;
		DecimalFormat df = new DecimalFormat("#.0000");
		for (int i = 0; i < resource.getMachineList().size(); i++) {
			pm = (PAMachine) resource.getMachineList().get(i);
			trace = pm.getExecutionTraceHashMap();
			Iterator<String> iter = trace.keySet().iterator();
			while (iter.hasNext()) {
				String gridletID = (String) iter.next();
				logger.trace("Machine ID\t" + pm.getMachineID()
						+ "\tis used by GridLet ID\t" + gridletID
						+ "\tbetween "
						+ df.format(trace.get(gridletID).getStartTime())
						+ "\tand\t"
						+ df.format(trace.get(gridletID).getEndTime())
						+ "\t,its deadline is\t"
						+ df.format(trace.get(gridletID).getDeadline_()));
			}
		}
	}

	public static void checkEnergyAndMissingRate(double missingRate,
			GridResource resource) {
		logger.trace("Simulation missing rate:\t" + missingRate);
		String turnOffPower = Configuration.getInstance().getParameterString(
				Configuration.Const.TURNOFF_POWER);
		double turnOffPowerValue = Configuration.Const.POWER_STATIQUE
				.equals(turnOffPower) ? PAMachine.POWERSTATIQUE : 0;
		PAMachineList paML = (PAMachineList) resource
				.getResourceCharacteristics().getMachineList();
		ArrayList<Double> durings = new ArrayList<Double>();
		double during = 0.0;
		double begin = 0.0;
		double end = 0.0;
		for (Double key : sortedTimePoints) {
			begin = end;
			end = key;
			during = end - begin;
			durings.add(during);
		}
		int j = 1;
		double energy = 0;
		for (Double key : sortedTimePoints) {
			double power = 0.0;
			for (int i = 0; i < paML.size(); i++) {
				PAMachineStatut statut = paML.get(i).getHistory().get(key);
				double addPower = (statut.getState_() == PAMachine.Const.STATE_ON ? paML
						.get(i).getPower(statut.getFrequency(),
								statut.getUtilization()) : turnOffPowerValue);
				power = power + addPower;
			}
			double dur = durings.size() > j ? power * durings.get(j) : 0;
			energy = energy + power * dur;
		}
		energy = energy / 1000000 / 3600;

		logger.trace("Simulation energy consumption:\t" + energy + "KW*h");
	}

	public static void checkMachinesUtilizationHistory(GridResource resource) {
		PAMachineList paML = (PAMachineList) resource
				.getResourceCharacteristics().getMachineList();
		for (Double key : sortedTimePoints) {
			String utilizationList = "";
			for (int i = 0; i < paML.size(); i++) {
				PAMachineStatut statut = paML.get(i).getHistory().get(key);
				utilizationList = utilizationList
						+ "\t"
						+ Configuration.getInstance().toDecimalFormat(
								statut.getUtilization());
			}
			logger.trace(utilizationList);
		}
	}

	public static void loadPowerLineChart(
			HashMap<Double, OptimizedResultEntry> optimizedUF,
			GridResource resource) {
		// Create Data
		DefaultCategoryDataset linedataset = new DefaultCategoryDataset();
		DefaultCategoryDataset linedataset2 = new DefaultCategoryDataset();
		// Line names
		String series1 = "Power";
		String series2 = "Workload";
		// horizontal axis name
		PAMachineList paML = (PAMachineList) resource
				.getResourceCharacteristics().getMachineList();
		String turnOffPower = Configuration.getInstance().getParameterString(
				Configuration.Const.TURNOFF_POWER);
		double turnOffPowerValue = Configuration.Const.POWER_STATIQUE
				.equals(turnOffPower) ? PAMachine.POWERSTATIQUE : 0;
		// TODO:missing some data at the end of the figure??

		for (Double key : sortedTimePoints) {
			double power = 0.0;
			for (int i = 0; i < paML.size(); i++) {
				PAMachineStatut statut = paML.get(i).getHistory().get(key);
				double addPower = (statut.getState_() == PAMachine.Const.STATE_ON ? paML
						.get(i).getPower(statut.getFrequency(),
								statut.getUtilization()) : turnOffPowerValue);
				power = power + addPower;
			}
			// TODO:automize power saving percentage calculating,and take
			// into
			// consideration the time interval
			linedataset.addValue(power, series1, key);
			linedataset2.addValue(
					ChangingWorkloadOptimizationAlgo.workload_.get(key),
					series2, key);
		}

		String unit = Configuration.getInstance().getParameterString(
				Configuration.Const.POWER_UNIT);
		JFreeChart chart = ChartFactory.createLineChart("Line Chart", // chart
																		// title
				"Time(s)", // domain axis label
				"Power(" + unit + ")", // range axis label
				linedataset, // data
				PlotOrientation.VERTICAL, // orientation
				true, // include legend
				true, // tooltips
				false // urls
				);
		CategoryPlot plot = chart.getCategoryPlot();
		// Customize the range axis...
		NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
		rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
		rangeAxis.setAutoRangeIncludesZero(true);
		rangeAxis.setUpperMargin(0.20);
		rangeAxis.setLabelAngle(Math.PI / 2.0);
		
		rangeAxis.setUpperBound(Configuration.getInstance().getParameterDouble(Configuration.Const.POWER_UPPER_BOUND));
		
		String axis2Name = "Workload("
				+ Configuration.getInstance().getParameterString(
						Configuration.Const.WORKLOAD_UNIT) + ")";
		NumberAxis axis2 = new NumberAxis(axis2Name);
		
		plot.setRangeAxis(2, axis2);
		plot.setRangeAxisLocation(1, AxisLocation.BOTTOM_OR_RIGHT);
		plot.setRangeAxisLocation(2, AxisLocation.BOTTOM_OR_RIGHT);
		plot.setDataset(2, linedataset2);
		plot.mapDatasetToRangeAxis(1, 1);
		plot.mapDatasetToRangeAxis(2, 2);
		
		CategoryItemRenderer renderer2 = new LineAndShapeRenderer();
		renderer2.setSeriesPaint(2, Color.BLUE);
		
		plot.setRenderer(2, renderer2);

		FileOutputStream out = null;
		File theDir = new File(Configuration.getInstance().getParameterString(
				Configuration.Const.REPORT_PATH));
		// if the directory does not exist, create it
		if (!theDir.exists()) {
			theDir.mkdir();
		}
		File fileName = new File(Configuration.getInstance()
				.getParameterString(Configuration.Const.REPORT_PATH),
				Configuration.getInstance().getParameterString(
						Configuration.Const.POWER_WORKLOAD_CHARTNAME)
						+ ".png");
		try {
			out = new FileOutputStream(fileName);
			try {
				ChartUtilities.writeChartAsPNG(out, chart, 1000, 500);
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		try {
			out.flush();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}

	public static void loadExecutionTraceGantt(ResourceCharacteristics rc) {
		String chartName = Configuration.getInstance().getParameterString(
				Configuration.Const.SIMULATIONGANT_CHARTNAME);
		File theDir = new File(Configuration.getInstance().getParameterString(
				Configuration.Const.REPORT_PATH));
		// if the directory does not exist, create it
		if (!theDir.exists()) {
			theDir.mkdir();
		}
		String fileName = new File(Configuration.getInstance()
				.getParameterString(Configuration.Const.REPORT_PATH), chartName
				+ ".png").getAbsolutePath();
		TaskSeriesCollection tsc = new TaskSeriesCollection();
		TaskSeries tsPlan = new TaskSeries("CPU occupied");
		for (int j = 0; j < rc.getMachineList().size(); j++) {
			PAMachine paM = (PAMachine) rc.getMachineList().get(j);
			ExecutionTraceHashMap trace = paM.getExecutionTraceHashMap();
			int macID = paM.getMachineID();
			long endSimulationTime;
			if (trace.size() != 0) {
				List<Map.Entry<String, SingleExecutionTrace>> list_Data = new ArrayList<Map.Entry<String, SingleExecutionTrace>>(
						trace.entrySet());
				endSimulationTime = (long) Collections
						.max(list_Data, new ComparatorExecutionEndTime())
						.getValue().getEndTime() * 1000;
				java.util.Date sDate = new Date(Const.START_SIMULATION_TIME);
				java.util.Date eDate = new Date(endSimulationTime);
				Task task = new Task(String.valueOf(macID), sDate, eDate);
				tsPlan.add(task);
				Iterator<?> iter = trace.entrySet().iterator();
				while (iter.hasNext()) {
					@SuppressWarnings("rawtypes")
					Map.Entry entry = (Map.Entry) iter.next();
					String gridletID = (String) entry.getKey();
					int repeatLimit = Configuration.getInstance()
							.getParameterInt(
									Configuration.Const.TASK_REPEAT_LIMIT);
					String gridletIDWithoutStar=gridletID.replaceAll("\\*", "");
					int paTaskId = Integer.valueOf(gridletIDWithoutStar) / repeatLimit;
					SingleExecutionTrace lt = (SingleExecutionTrace) entry
							.getValue();
					long startDate = Double.valueOf(lt.getStartTime() * 1000)
							.longValue();
					long endDate = Double.valueOf(lt.getEndTime() * 1000)
							.longValue();
					String uniqueTaskID = String.valueOf(macID) + "_"
							+ String.valueOf(gridletID);
					java.util.Date ssDate = new Date(Long.valueOf(startDate));
					java.util.Date seDate = new Date(Long.valueOf(endDate));
					if (ssDate.before(seDate)) {
						Task subTask = new Task(uniqueTaskID, ssDate, seDate);
						task.addSubtask(subTask);
						subTask.setDescription(String.valueOf(paTaskId));
					}
				}
			} else {
				java.util.Date sDate = new Date(Const.START_SIMULATION_TIME);
				java.util.Date eDate = new Date(Const.START_SIMULATION_TIME);
				Task t = new Task(String.valueOf(macID), sDate, eDate);
				tsPlan.add(t);
			}
		}
		tsc.add(tsPlan);
		JFreeChart chart = ChartFactory.createGanttChart(
				"Jobs Execution Gantt", "Node No.", "Execution time(minute)",
				tsc, false, true, true);
		FileOutputStream out = null;
		Font fTitle = new Font("Times New Roman", Font.BOLD, 30);
		Font fVertical = new Font("Times New Roman",Font.CENTER_BASELINE, 20);
		Font fLabel=new Font("Times New Roman", Font.BOLD, 20);
		chart.getTitle().setFont(fTitle);
		CategoryPlot plot = chart.getCategoryPlot();
		GanttRendererWithColor renderer = new GanttRendererWithColor(tsc);
		plot.setRenderer(renderer);

		renderer.setBaseItemLabelsVisible(true);
		renderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(
				ItemLabelAnchor.CENTER, TextAnchor.CENTER_LEFT));
		renderer.setBaseItemLabelFont(fLabel);
		CategoryAxis domainAxis = plot.getDomainAxis();
		domainAxis.setLabelFont(fVertical);
		domainAxis.setTickLabelFont(fVertical);
		ValueAxis rangeAxis = plot.getRangeAxis();
		rangeAxis.setLabelFont(fVertical);
		rangeAxis.setTickLabelFont(fVertical);
		try {
			out = new FileOutputStream(fileName);
			try {
				ChartUtilities.writeChartAsPNG(out, chart, 1000, 500);
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		try {
			out.flush();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
	}
}
